package com.skynet.supervision.common;

import cn.hutool.core.text.StrFormatter;
import com.skynet.supervision.exceptions.CheckedException;
import com.skynet.supervision.utils.Strings;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import lombok.val;

import javax.annotation.Nonnull;
import java.io.Serial;
import java.io.Serializable;
import java.util.Optional;
import java.util.function.Consumer;

@EqualsAndHashCode(callSuper = true)
@ToString
@SuppressWarnings("unused")
public class Result extends ResultGeneric<Object> implements Serializable {

    @Serial
    private static final long serialVersionUID = 1L;

    private static final String msg = "success";

    public Result() {
        super();
    }

    public Result(int code, String message, Object data) {
        super(code, message, data);
    }

    public static Result newResult(int code, String message, Object data) {
        return new Result(code, message, data);
    }

    public static Result newResult(int code, String message) {
        return newResult(code, message, null);
    }

    public static Result newResult() {
        return new Result(ErrCode.OK.getCode(), ErrCode.OK.getMessage(), null);
    }

    public static Result newResult(ErrCode code, String message, Object data) {
        return newResult(code.getCode(), message, data);
    }

    public static Result newResult(ErrCode code, Object data) {
        return newResult(code.getCode(), code.getMessage(), data);
    }

    public static Result newResult(ErrCode code, String message) {
        return newResult(code.getCode(), message, null);
    }

    public static Result newResult(ErrCode code) {
        return newResult(code.getCode(), code.getMessage(), null);
    }

    public static Result newNormalErr(String message) {
        return newResult(ErrCode.NORMAL_ERROR.getCode(), message, null);
    }

    public static Result success() {
        return wrapGeneric(success(ErrCode.OK.getMessage(), msg));
    }

    public static Result success(Object data) {
        return wrapGeneric(success(ErrCode.OK.getMessage(), data));
    }

    public static Result success(String data) {
        return wrapGeneric(success(ErrCode.OK.getMessage(), data));
    }

    public static Result failure(int code, String message) {
        return wrapGeneric(newResult(code, message));
    }

    public static Result failure(int code, String fmt, Object... args) {
        return failure(code, Strings.sprintf(fmt, args));
    }

    public static Result failure(ErrCode code, String message) {
        return failure(code.getCode(), message);
    }

    public static Result failure(ErrCode code, String fmt, Object... args) {
        return failure(code, Strings.sprintf(fmt, args));
    }

    public static Result failure(int code) {
        return failure(code, "请求失败");
    }

    public static Result failure(ErrCode code) {
        return failure(code.getCode(), code.getMessage());
    }

    public static Result failure(String message) {
        return failure(ErrCode.NORMAL_ERROR, message);
    }

    public static Result failure(String fmt, Object... args) {
        return failure(ErrCode.NORMAL_ERROR, fmt, args);
    }

    public static Result failure() {
        return failure(ErrCode.NORMAL_ERROR);
    }

    public CheckedException wrap() {
        return CheckedException.wrap(this);
    }

    public void throwIfError() throws CheckedException {
        throwIfError(null);
    }

    public void throwIfError(Consumer<Result> custom) throws CheckedException {
        if (hasError()) {
            Optional.ofNullable(custom).ifPresent(v -> v.accept(this));
            throw this.wrap();
        }
    }

    @Override
    public Result withCode(int code) {
        this.code = code;
        return this;
    }

    @Override
    public Result withMessage(String message) {
        this.message = message;
        return this;
    }

    @Override
    public Result wrapMessage(String externalMessage) {
        this.message = StrFormatter.format("{}: caused by {}", externalMessage, message);
        return this;
    }

    @Override
    public Result withData(Object data) {
        this.data = data;
        return this;
    }

    public static Result notImplement() {
        val stackElement = new Throwable().getStackTrace()[1];
        val invokeMethodName = stackElement.getMethodName();
        return Result.newResult(ErrCode.NOT_IMPL_YET, StrFormatter.format("该函数:{}尚未实现", invokeMethodName));
    }

    public static Result wrapGeneric(@Nonnull ResultGeneric<?> bg) {
        return Result.newResult(bg.code, bg.message, bg.data);
    }
}
